package com.hmy.finance.service.statement.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmy.ccp.common.dto.BaseDto;
import com.hmy.ccp.common.dto.ContextUserInfo;
import com.hmy.ccp.common.response.BasePageResponse;
import com.hmy.ccp.error.exception.HMYRuntimeException;
import com.hmy.ccp.starter.web.utils.UserContextUtil;
import com.hmy.contract.bo.request.contract.ContractActorRequest;
import com.hmy.contract.bo.request.contract.ContractBusinessInfoRequest;
import com.hmy.contract.bo.request.contract.ContractSignTaskCancelApiRequest;
import com.hmy.contract.bo.request.contract.CreateContractRequest;
import com.hmy.contract.bo.response.contract.CreateContractResponse;
import com.hmy.contract.commons.enums.ContractActorTypeEnum;
import com.hmy.contract.commons.enums.ContractBusinessTypeEnum;
import com.hmy.contract.commons.enums.ContractFileModelEnum;
import com.hmy.contract.commons.enums.ContractTypeEnum;
import com.hmy.finance.api.bo.core.request.FinancialContractUpdateApiRequest;
import com.hmy.finance.api.bo.core.request.enums.StatementActionTypeEnum;
import com.hmy.finance.api.bo.core.request.enums.StatementTypeEnum;
import com.hmy.finance.bo.request.*;
import com.hmy.finance.bo.response.*;
import com.hmy.finance.common.constants.StatementLossTypeContants;
import com.hmy.finance.common.enums.BusinessBillTypeEnum;
import com.hmy.finance.common.enums.ProfitLossStatusEnum;
import com.hmy.finance.common.enums.StatementStatusEnum;
import com.hmy.finance.common.oss.OssStsUtil;
import com.hmy.finance.dal.mapper.*;
import com.hmy.finance.dal.mapper.receiveAdjust.ReceiveAdjustMapper;
import com.hmy.finance.dal.po.*;
import com.hmy.finance.dal.po.json.ReconciliationAdjustJson;
import com.hmy.finance.dal.po.json.WaiverRuleJson;
import com.hmy.finance.dal.po.receiveAdjust.ReceiveAdjustPo;
import com.hmy.finance.manager.contract.ContractFeignClientRpc;
import com.hmy.finance.manager.fulfillment.FulfillmentFeignClientRpc;
import com.hmy.finance.manager.order.OrderFeignClientRpc;
import com.hmy.finance.service.statement.IReconciliationStatementProviderService;
import com.hmy.finance.service.statement.dto.*;
import com.hmy.finance.service.rdto.BasePageResultRdto;
import com.hmy.finance.service.statement.IReconciliationStatementService;
import com.hmy.finance.service.statement.rdto.HistoryStatementQueryRdto;
import com.hmy.finance.service.statement.statementHandlerFactory.*;
import com.hmy.fulfillment.api.bo.request.CountUnFinishClaimRequest;
import com.hmy.fulfillment.api.bo.request.CountUnFinishPauseRequest;
import com.hmy.fulfillment.api.bo.request.ExitingFormCountRequest;
import com.hmy.fulfillment.api.bo.response.ExitingFormCountResponse;
import com.hmy.fulfillment.api.common.enums.RoleEnum;
import com.hmy.order.api.bo.response.OrderPrimaryApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFPicture;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author songkf
 * @description 针对表【reconciliation_statement(对账单)】的数据库操作Service实现
 * @createDate 2024-02-21 18:55:01
 */

@Slf4j
@Service
public class ReconciliationStatementServiceImpl implements IReconciliationStatementService {

    @Resource
    private IdentifierGenerator identifierGenerator;

    @Resource
    private HistoryStatementHandler historyStatementHandler;

    @Resource
    private MonthStatementHandler monthStatementHandler;

    @Resource
    private PeriodStatementHandler periodStatementHandler;

    @Resource
    private FlexibleStatementHandler flexibleStatementHandler;

    @Resource
    private ReconciliationStatementMapper reconciliationStatementMapper;

    @Resource
    private ReconciliationDeviceStatementMapper reconciliationDeviceStatementMapper;

    @Resource
    private ReceivableInventoryMapper receivableInventoryMapper;

    @Resource
    private DeviceRentalRecordMapper deviceRentalRecordMapper;

    @Resource
    private ReceiveAdjustMapper receiveAdjustMapper;

    @Resource
    private OrderFeignClientRpc orderFeignClientRpc;

    @Resource
    private ContractFeignClientRpc contractFeignClientRpc;

    @Resource
    private FulfillmentFeignClientRpc fulfillmentFeignClientRpc;

    @Resource
    private ProfitLossRecordMapper profitLossRecordMapper;

    @Resource
    private DeviceProfitLossDetailMapper deviceProfitLossDetailMapper;


    @Resource
    private IReconciliationStatementProviderService iReconciliationStatementProviderService;

    @Resource
    private OssStsUtil ossStsUtil;

    @Override
    public BasePageResponse<HistoryStatementQueryResponse> queryStatementsByPage(HistoryStatementQueryRequest historyStatementQueryRequest) {
        HistoryStatementQueryDto historyStatementQueryDto = BeanUtil.copyProperties(historyStatementQueryRequest, HistoryStatementQueryDto.class);

        //根据各种角色查询的数据不一样
        setUserInfoContextInfo(historyStatementQueryDto);

        /*查询条件组合*/
        LambdaQueryWrapper<ReconciliationStatementPo> queryWrapper = new LambdaQueryWrapper<>();
        packageQueryWrapper(historyStatementQueryDto, queryWrapper);
        BasePageResultRdto<HistoryStatementQueryRdto> pageResult = selectOrderPrimaryRecords(historyStatementQueryDto.getPageNum(), historyStatementQueryDto.getPageSize(), queryWrapper);

        /*封装为ReceivablesQueryResponse*/
        BasePageResponse<HistoryStatementQueryResponse> response = new BasePageResponse<>();
        response.setRecords(BeanUtil.copyToList(pageResult.getRecords(), HistoryStatementQueryResponse.class));
        response.setTotal(pageResult.getTotal());
        response.setPages(pageResult.getPages());
        response.setSize(pageResult.getSize());
        response.setCurrent(pageResult.getCurrent());
        return response;
    }

    private void packageQueryWrapper(HistoryStatementQueryDto historyStatementQueryDto, LambdaQueryWrapper<ReconciliationStatementPo> queryWrapper) {
        /*根据不同角色设置不同查询数据权限*/
        if (Objects.nonNull(historyStatementQueryDto.getSaleManagerId())) {
            queryWrapper.eq(ReconciliationStatementPo::getSaleManagerId, historyStatementQueryDto.getSaleManagerId());
        }
        if (CollectionUtil.isNotEmpty(historyStatementQueryDto.getDeptIds())) {
            queryWrapper.in(ReconciliationStatementPo::getDeptId, historyStatementQueryDto.getDeptIds());
        }
        if (Objects.nonNull(historyStatementQueryDto.getCustomerId())) {
            queryWrapper.eq(ReconciliationStatementPo::getCustomerId, historyStatementQueryDto.getCustomerId());
        }
        if (Objects.nonNull(historyStatementQueryDto.getSignatoryId())) {
            queryWrapper.eq(ReconciliationStatementPo::getSignatoryId, historyStatementQueryDto.getSignatoryId());
        }
        if (Objects.nonNull(historyStatementQueryDto.getContractId())) {
            queryWrapper.eq(ReconciliationStatementPo::getOrderContractId, historyStatementQueryDto.getContractId());
        }
        queryWrapper.in(ReconciliationStatementPo::getStatementStatus, Arrays.asList(StatementStatusEnum.NOT_SIGNED, StatementStatusEnum.SIGNED));//只查询未签署和已签署的账单
    }

    private BasePageResultRdto<HistoryStatementQueryRdto> selectOrderPrimaryRecords(Integer pageNum, Integer pageSize, LambdaQueryWrapper<ReconciliationStatementPo> queryWrapper) {
        Page<ReconciliationStatementPo> page = new Page<>(pageNum, pageSize);
        queryWrapper.orderByDesc(ReconciliationStatementPo::getCreateTime);//按创建时间倒序排序

        Page<ReconciliationStatementPo> pageResult = reconciliationStatementMapper.selectPage(page, queryWrapper);
        /*封装结果集*/
        List<HistoryStatementQueryRdto> historyStatementQueryRdtos = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(pageResult.getRecords())) {
            historyStatementQueryRdtos = BeanUtil.copyToList(pageResult.getRecords(), HistoryStatementQueryRdto.class);
            /*可以在此处封装一些业务逻辑或者其他信息，不会破坏各个接口级别的response和dto的封装性*/
            historyStatementQueryRdtos.forEach(historyStatementQueryRdto -> {
                historyStatementQueryRdto.setStatementStatusCode(historyStatementQueryRdto.getStatementStatus().getValue());
                historyStatementQueryRdto.setStatementStatusDesc(historyStatementQueryRdto.getStatementStatus().getDesc());
                historyStatementQueryRdto.setStatementStatusCssTag(historyStatementQueryRdto.getStatementStatus().getCssTag());
                historyStatementQueryRdto.setStatementTypeCode(historyStatementQueryRdto.getStatementType().getValue());
                historyStatementQueryRdto.setStatementTypeDesc(historyStatementQueryRdto.getStatementType().getDesc());
            });
        }
        BasePageResultRdto<HistoryStatementQueryRdto> basePageResult = new BasePageResultRdto<>();
        basePageResult.setRecords(historyStatementQueryRdtos);
        basePageResult.setTotal(pageResult.getTotal());
        basePageResult.setPages(pageResult.getPages());
        basePageResult.setSize(pageResult.getSize());
        basePageResult.setCurrent(pageResult.getCurrent());
        return basePageResult;
    }


    @Override
    public List<ContractDeviceInfoResponse> queryContractDevicesInfo(ContractDevicesInfoQueryRequest contractDevicesInfoQueryRequest) {
        log.info("queryContractDevicesInfo request:{}", JSON.toJSONString(contractDevicesInfoQueryRequest));
        List<ContractDeviceInfoResponse> contractDeviceInfoResponses = new ArrayList<>();

        /*step1.设备租赁信息表查询合同及设备信息;*/
        LambdaQueryWrapper<DeviceRentalRecordPo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeviceRentalRecordPo::getContractId, contractDevicesInfoQueryRequest.getContractId());
        queryWrapper.eq(DeviceRentalRecordPo::getIsDeleted, 0);
        List<DeviceRentalRecordPo> contractDevices = deviceRentalRecordMapper.selectList(queryWrapper);

        /*查询应收盘点表信息*/
        LambdaQueryWrapper<DeviceProfitLossDetailPo> deviceProfitLossQueryWrapper = new LambdaQueryWrapper<>();
        deviceProfitLossQueryWrapper.eq(DeviceProfitLossDetailPo::getContractId, contractDevicesInfoQueryRequest.getContractId());
        deviceProfitLossQueryWrapper.eq(DeviceProfitLossDetailPo::getIsDeleted, 0);
        List<DeviceProfitLossDetailPo> deviceProfitLossDetailPos = deviceProfitLossDetailMapper.selectList(deviceProfitLossQueryWrapper);


        if (CollectionUtil.isEmpty(contractDevices)) {
            return contractDeviceInfoResponses;
        }
        /*step2.组合数据;*/
        //将contractDevices转换为以contractId进行分组的map
        Map<Long, List<DeviceRentalRecordPo>> entryDeviceModelMaps = contractDevices.stream().collect(Collectors.groupingBy(DeviceRentalRecordPo::getContractId));
        //遍历entryDeviceModelMaps，组合数据
        entryDeviceModelMaps.forEach((contractId, deviceRentalRecordPos) -> {
            ContractDeviceInfoResponse contractDeviceInfoResponse = new ContractDeviceInfoResponse();
            contractDeviceInfoResponse.setContractId(contractId);
            contractDeviceInfoResponse.setContractNo(deviceRentalRecordPos.get(0).getContractExtensionInfo().getContractNo());
            contractDeviceInfoResponse.setOrderId(deviceRentalRecordPos.get(0).getOrderId());
            contractDeviceInfoResponse.setEntryFormId(deviceRentalRecordPos.get(0).getEntryFormId());

            /*对运费减免规则进行封装*/
            List<WaiverRuleJson> shippingWaiverRules = deviceRentalRecordPos.get(0).getEntryShippingInfo().getShippingWaiverRuleList();
            if (CollectionUtil.isNotEmpty(shippingWaiverRules)) {
                //对modificationWaiverRules按modificationWaiverDays进行排序
                shippingWaiverRules.sort(Comparator.comparing(WaiverRuleJson::getWaiverDays));
                StringBuffer shippingWaiverRuleDesc = new StringBuffer("运费减免条件：");
                AtomicReference<Double> totalShippingWaiverRate = new AtomicReference<>(0.0);
                shippingWaiverRules.forEach(waiverRuleJson -> {
                    totalShippingWaiverRate.updateAndGet(v -> v + waiverRuleJson.getWaiverRate());
                    shippingWaiverRuleDesc.append("满" + waiverRuleJson.getWaiverDays() + "天改装减免" + (totalShippingWaiverRate.get() * 100) + "%;");
                });
                contractDeviceInfoResponse.setShippingWaiverRuleDesc(String.valueOf(shippingWaiverRuleDesc));
            }

            /*对改装减免规则进行封装*/
            List<WaiverRuleJson> modificationWaiverRules = deviceRentalRecordPos.get(0).getModificationFeeInfo().getModificationWaiverRuleList();
            if (CollectionUtil.isNotEmpty(modificationWaiverRules)) {
                //对modificationWaiverRules按modificationWaiverDays进行排序,正序
                modificationWaiverRules.sort(Comparator.comparing(WaiverRuleJson::getWaiverDays));
                StringBuffer modificationWaiverRuleDesc = new StringBuffer("改装减免条件：");
                AtomicReference<Double> totalModificationWaiverRate = new AtomicReference<>(0.0);
                modificationWaiverRules.forEach(waiverRuleJson -> {
                    totalModificationWaiverRate.updateAndGet(v -> v + waiverRuleJson.getWaiverRate());
                    modificationWaiverRuleDesc.append("满" + waiverRuleJson.getWaiverDays() + "天改装减免" + (totalModificationWaiverRate.get() * 100) + "%;");
                });
                contractDeviceInfoResponse.setModificationWaiverRuleDesc(String.valueOf(modificationWaiverRuleDesc));
            }

            List<ContractDeviceDetailResponse> deviceDetails = new ArrayList<>();
            deviceRentalRecordPos.forEach(deviceRentalRecordPo -> {
                ContractDeviceDetailResponse contractDeviceDetailResponse = new ContractDeviceDetailResponse();
                Map<String, BigDecimal> deviceProfitLossMap = new HashMap<>();
                List<DeviceProfitLossDetailPo> deviceProfitLossPos = deviceProfitLossDetailPos.stream().filter(deviceProfitLossDetailPo -> deviceProfitLossDetailPo.getDeviceRentalRecordId().equals(deviceRentalRecordPo.getId())).collect(Collectors.toList());
                BigDecimal entryShippingFee = iReconciliationStatementProviderService.getProfitLossSubtotalWithDeviceLoss(deviceProfitLossPos, StatementLossTypeContants.ENTRY_SHIPPING_MAPPING);
                deviceProfitLossMap.put("entryShippingFee", entryShippingFee);
                BigDecimal exitShippingFee = iReconciliationStatementProviderService.getProfitLossSubtotalWithDeviceLoss(deviceProfitLossPos, StatementLossTypeContants.EXIT_SHIPPING_MAPPING);
                deviceProfitLossMap.put("exitShippingFee", exitShippingFee);
                BigDecimal modificationPayable = iReconciliationStatementProviderService.getProfitLossSubtotalWithDeviceLoss(deviceProfitLossPos, StatementLossTypeContants.ENTRY_MODIFICATION_MAPPING);
                deviceProfitLossMap.put("modificationPayable", modificationPayable);
                contractDeviceDetailResponse.convertToContractDeviceInfoResponse(contractDeviceDetailResponse, deviceRentalRecordPo, deviceProfitLossMap);
                deviceDetails.add(contractDeviceDetailResponse);
            });
            contractDeviceInfoResponse.setDeviceDetails(deviceDetails);
            contractDeviceInfoResponses.add(contractDeviceInfoResponse);
        });
        return contractDeviceInfoResponses;
    }

    @Override
    @Transactional
    public DeleteStatementResponse deleteStatement(DeleteStatementRequest deleteStatementRequest) {
        log.info("deleteStatement request:{}", JSON.toJSONString(deleteStatementRequest));
        DeleteStatementDto deleteStatementDto = BeanUtil.copyProperties(deleteStatementRequest, DeleteStatementDto.class);
        /*step1.查询账单状态，已签署的账单不允许删除*/
        LambdaQueryWrapper<ReconciliationStatementPo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ReconciliationStatementPo::getStatementId, deleteStatementDto.getStatementId());
        queryWrapper.eq(ReconciliationStatementPo::getIsDeleted, 0);
        ReconciliationStatementPo reconciliationStatementRo = reconciliationStatementMapper.selectOne(queryWrapper);
        if (Objects.isNull(reconciliationStatementRo)) {
            throw new HMYRuntimeException(506300008, new Object[]{deleteStatementDto.getStatementId().toString()});//账单不存在
        }
        if (StatementStatusEnum.SIGNED.equals(reconciliationStatementRo.getStatementStatus())) {
            throw new HMYRuntimeException(506300008);//已签署的账单不允许删除
        }
        if (StringUtils.isEmpty(reconciliationStatementRo.getContractCreatedUrl())) {
            throw new HMYRuntimeException(506300010);//账单未生成合同，不允许删除
        }


        deleteStatementDto.setContractId(reconciliationStatementRo.getContractId());
        setUserContextInfo(deleteStatementDto);

        /*step2.删除账单*/
        LambdaQueryWrapper<ReconciliationStatementPo> updateWrapper = new LambdaQueryWrapper<>();
        updateWrapper.eq(ReconciliationStatementPo::getStatementId, deleteStatementDto.getStatementId());
        Integer deleteFlag = reconciliationStatementMapper.delete(updateWrapper);
        if (deleteFlag > 0) {
            LambdaQueryWrapper<ReconciliationDeviceStatementPo> deviceDetailWrapper = new LambdaQueryWrapper<>();
            deviceDetailWrapper.eq(ReconciliationDeviceStatementPo::getReconciliationStatementId, deleteStatementDto.getStatementId());
            deviceDetailWrapper.eq(ReconciliationDeviceStatementPo::getIsDeleted, 0);
            Integer deviceDeleteFlag = reconciliationDeviceStatementMapper.delete(deviceDetailWrapper);

            /*step4.删除合同签约信息，不让签约*/
            ContractSignTaskCancelApiRequest request = new ContractSignTaskCancelApiRequest();
            request.setContractId(deleteStatementDto.getContractId());
            request.setTerminationNote("账单已删除，不再需要签署,删除人：" + deleteStatementDto.getCreatorName());
            contractFeignClientRpc.cancelContract(request);
        }

        DeleteStatementResponse deleteStatementResponse = new DeleteStatementResponse();
        deleteStatementResponse.setStatementId(deleteStatementDto.getStatementId());
        deleteStatementResponse.setSuccess(Boolean.TRUE);
        return deleteStatementResponse;
    }

    @Override
    public CreateStatementValidationResponse validationCreateStatement(CreateStatementValidationRequest createStatementValidationRequest) {
        log.info("validationCreateStatement request:{}", JSON.toJSONString(createStatementValidationRequest));
        CreateStatementValidationResponse createStatementValidationResponse = new CreateStatementValidationResponse();
        CreateStatementValidationDto createStatementValidationDto = BeanUtil.copyProperties(createStatementValidationRequest, CreateStatementValidationDto.class);

        List<ReconciliationDeviceStatementPo> list = new ArrayList<>();
        createStatementValidationDto.getDeviceDetails().forEach(d -> {
            ReconciliationDeviceStatementPo reconciliationDeviceStatementPo = new ReconciliationDeviceStatementPo();
            reconciliationDeviceStatementPo.setContractId(d.getContractId());
            reconciliationDeviceStatementPo.setDeviceId(d.getDeviceId());
            reconciliationDeviceStatementPo.setDeviceRentalRecordId(d.getDeviceRentalRecordId());
            list.add(reconciliationDeviceStatementPo);
        });
        String errorMessage = statementDeviceChecker(list);
        if (StringUtils.isNotEmpty(errorMessage)) {
            createStatementValidationResponse.setSuccess(Boolean.FALSE);
            createStatementValidationResponse.setErrorMessage(errorMessage);
            return createStatementValidationResponse;
        }
        createStatementValidationResponse.setSuccess(Boolean.TRUE);
        return createStatementValidationResponse;
    }

    private String statementDeviceChecker(List<ReconciliationDeviceStatementPo> list) {
        String errorMsg = null;

        List<ReconciliationStatementRo> reconciliationDeviceStatementRos = reconciliationDeviceStatementMapper.selectRecordsByContractIdsAndDeviceIds(list);
        if (CollectionUtil.isNotEmpty(reconciliationDeviceStatementRos)) {
            StringBuffer statementNoStrBuffer = new StringBuffer();
            reconciliationDeviceStatementRos.stream().map(ReconciliationStatementRo::getStatementNo).distinct().forEach(c -> {
                statementNoStrBuffer.append(c).append(",");
            });
            String statementNoStr = statementNoStrBuffer.toString().substring(0, statementNoStrBuffer.length() - 1);//去除最后一个","

            StringBuffer contractNoStrBuffer = new StringBuffer();
            reconciliationDeviceStatementRos.stream().map(ReconciliationStatementRo::getContractNo).distinct().forEach(c -> {
                contractNoStrBuffer.append(c).append(",");
            });
            String contractNoStr = contractNoStrBuffer.toString().substring(0, contractNoStrBuffer.length() - 1);//去除最后一个","

            StringBuffer deviceNoStrBuffer = new StringBuffer();
            reconciliationDeviceStatementRos.forEach(d -> {
                deviceNoStrBuffer.append(d.getDeviceNo()).append(",");
            });
            String deviceNoStr = deviceNoStrBuffer.toString().substring(0, deviceNoStrBuffer.length() - 1);//去除最后一个","


            errorMsg = "合同号：" + contractNoStr + ",设备号：" + deviceNoStr + "已存在待签署的账单（账单序号:" + statementNoStr + "),请联系客户完成签署或删除该账单后再拉取新账单。";
//            throw new HMYRuntimeException(506300009, new Object[]{contractNoStr, deviceNoStr});
        }
        return errorMsg;
    }


    @Override
    @Transactional
    public CreateStatementResponse createStatement(CreateStatementRequest createStatementRequest) {
        CreateStatementResponse createStatementResponse = new CreateStatementResponse();
        log.info("createStatement request:{}", JSON.toJSONString(createStatementRequest));
        Map<String, DefaultStatementHandler> statementHandlerMap = new HashMap<>(16);
        statementHandlerMap.put(StatementTypeEnum.MONTHLY.getValue(), monthStatementHandler);
        statementHandlerMap.put(StatementTypeEnum.PERIODIC.getValue(), periodStatementHandler);
        statementHandlerMap.put(StatementTypeEnum.FLEXIBLE.getValue(), flexibleStatementHandler);
        statementHandlerMap.put(StatementTypeEnum.TOTAL.getValue(), historyStatementHandler);

        CreateStatementDto createStatementDto = BeanUtil.copyProperties(createStatementRequest, CreateStatementDto.class);
        setUserContextInfo(createStatementDto);

        //判断当前登录人是否为销售经理，如果是设置销售经理ID、name和部门id
        if (createStatementDto.getRoles().contains(RoleEnum.SALE_MANAGER.getRoleType())) {
            createStatementDto.setSaleManagerId(createStatementDto.getCreatorId());
            createStatementDto.setSaleManagerName(createStatementDto.getCreatorName());
            createStatementDto.setSaleManagerDeptId(createStatementDto.getDeptIds().get(0));
        }

        List<CreateStatementDeviceDto> deviceDetails = createStatementDto.getDeviceDetails();
        /*判断是否存在待签署的账单*/
        if (StatementActionTypeEnum.SIGN.getValue().equals(createStatementDto.getActionType()) || StatementActionTypeEnum.SAVE.getValue().equals(createStatementDto.getActionType())) {
            List<ReconciliationDeviceStatementPo> list = new ArrayList<>();
            deviceDetails.forEach(d -> {
                ReconciliationDeviceStatementPo reconciliationDeviceStatementPo = new ReconciliationDeviceStatementPo();
                reconciliationDeviceStatementPo.setContractId(d.getContractId());
                reconciliationDeviceStatementPo.setDeviceId(d.getDeviceId());
                reconciliationDeviceStatementPo.setDeviceRentalRecordId(d.getDeviceRentalRecordId());
                list.add(reconciliationDeviceStatementPo);
            });
            //如果存在，则抛出异常信息
            String errorMessage = statementDeviceChecker(list);
            if (StringUtils.isNotEmpty(errorMessage)) {
                createStatementResponse.setErrorMessage(errorMessage);
                return createStatementResponse;
            }
        }

        List<DeviceRentalRecordPo> deviceRentalRecordParams = new ArrayList<>();
        deviceDetails.forEach(d -> {
            DeviceRentalRecordPo deviceRentalRecordPo = new DeviceRentalRecordPo();
            deviceRentalRecordPo.setId(d.getDeviceRentalRecordId());
            deviceRentalRecordPo.setDeviceNo(d.getDeviceNo());
            deviceRentalRecordPo.setContractNo(d.getContractNo());
            deviceRentalRecordParams.add(deviceRentalRecordPo);
        });
        List<DeviceRentalRecordPo> deviceRentalRecordPos = deviceRentalRecordMapper.selectDeviceByDeviceNoAndContractNo(deviceRentalRecordParams);

        /*设置签约的手机和身份证号*/
        deviceRentalRecordPos.stream().filter(deviceRentalRecordPo -> deviceRentalRecordPo.getCustomerExtensionInfo().getSignatoryId().equals(createStatementDto.getSignatoryId())).findFirst().ifPresent(deviceRentalRecordPo -> {
            createStatementDto.setSignatoryPhone(deviceRentalRecordPo.getCustomerExtensionInfo().getSignatoryPhone());
            createStatementDto.setSignatoryIdCardNo(deviceRentalRecordPo.getCustomerExtensionInfo().getSignatoryIdCardNumber());
        });

        if (StringUtils.isEmpty(createStatementDto.getSignatoryIdCardNo()) || StringUtils.isEmpty(createStatementDto.getSignatoryPhone())) {
            throw new HMYRuntimeException(506300007);
        }

        /*账单dto封装*/
        StringBuffer projectName = new StringBuffer();
        projectName.append(deviceRentalRecordPos.get(0).getOrderExtensionInfo().getProjectName());
        LocalDateTime expenseCycleEndTime = createStatementDto.getStatementEndTime();

        for (CreateStatementDeviceDto d : deviceDetails) {
//            d.setSetPreStartTime(createStatementDto.getStatementStartTime());//todo
//            d.setSetPreEndTime(createStatementDto.getStatementEndTime());//设置设备纬度予定的账单开始时间和结束时间

            Optional<DeviceRentalRecordPo> deviceRentalRecordPoOptional = deviceRentalRecordPos.stream().filter(deviceRentalRecordPo -> d.getDeviceRentalRecordId().equals(deviceRentalRecordPo.getId())).findFirst();
            if (deviceRentalRecordPoOptional.isPresent()) {
                DeviceRentalRecordPo deviceRentalRecordPo = deviceRentalRecordPoOptional.get();
                d.setContractId(deviceRentalRecordPo.getContractId());
                d.setContractNo(deviceRentalRecordPo.getContractNo());
                d.setDeviceId(deviceRentalRecordPo.getDeviceId());
                d.setDeviceNo(deviceRentalRecordPo.getDeviceNo());
                d.setDeviceModel(deviceRentalRecordPo.getDeviceExtensionInfo().getDeviceModel());
                d.setDeviceStatementStatusDesc(Objects.nonNull(deviceRentalRecordPo.getRentalEndTime()) ? "退租" : "在租");
                d.setRentalUnitPrice(deviceRentalRecordPo.getUseMonthlyRentalPrice() ? deviceRentalRecordPo.getMonthlyRentalPrice().divide(new BigDecimal(30), 2, RoundingMode.HALF_UP) : deviceRentalRecordPo.getDailyRentalPrice());
                d.setRentalPrice(deviceRentalRecordPo.getUseMonthlyRentalPrice() ? deviceRentalRecordPo.getMonthlyRentalPrice() : deviceRentalRecordPo.getDailyRentalPrice());
                d.setMonthlyRentalPrice(deviceRentalRecordPo.getMonthlyRentalPrice());
                d.setDailyRentalPrice(deviceRentalRecordPo.getDailyRentalPrice());
                if (deviceRentalRecordPo.getLastReconciliationTime().isEqual(deviceRentalRecordPo.getRentalStartTime())) {
                    d.setLastReconciliationTime(deviceRentalRecordPo.getLastReconciliationTime());
                } else {
                    d.setLastReconciliationTime(deviceRentalRecordPo.getLastReconciliationTime().plusSeconds(1));
                }
                d.setUseMonthlyRentalPrice(deviceRentalRecordPo.getUseMonthlyRentalPrice());
                d.setDeviceBeginRentTime(deviceRentalRecordPo.getRentalStartTime());
                d.setDeviceEndRentTime(deviceRentalRecordPo.getRentalEndTime());
                d.setExpenseCycleEndTime(deviceRentalRecordPo.getExpenseCycleEndTime());
                //对比expenseCycleEndTime和deviceRentalRecordPo.getExpenseCycleEndTime()，取最大值
                if (Objects.nonNull(d.getExpenseCycleEndTime()) && expenseCycleEndTime.isBefore(d.getExpenseCycleEndTime())) {
                    expenseCycleEndTime = d.getExpenseCycleEndTime();
                }
                d.setContractExtensionInfo(deviceRentalRecordPo.getContractExtensionInfo());
                d.setCustomerExtensionInfo(deviceRentalRecordPo.getCustomerExtensionInfo());
            }
        }
        createStatementDto.setExpenseCycleEndTime(expenseCycleEndTime);
        createStatementDto.setProjectName(projectName.toString());
        createStatementDto.setSaleManagerId(deviceRentalRecordPos.get(0).getOrderExtensionInfo().getSaleManagerId());
        createStatementDto.setSaleManagerName(deviceRentalRecordPos.get(0).getOrderExtensionInfo().getSaleManagerName());
        createStatementDto.setSaleManagerDeptId(deviceRentalRecordPos.get(0).getOrderExtensionInfo().getSaleManagerDeptId());

        log.info("createStatement createStatementDto:{}", JSON.toJSONString(createStatementDto));
        /*按账单类型执行不同的账单处理器*/
        ReconciliationStatementDto reconciliationStatementDto = statementHandlerMap.get(createStatementDto.getStatementType()).doStatementHandler(createStatementDto);
        //对reconciliationStatementDto进行一些通用数据封装
        reconciliationStatementDto.setCustomerId(createStatementDto.getCustomerId());
        reconciliationStatementDto.setCustomerName(createStatementDto.getCustomerName());
        reconciliationStatementDto.setSignatoryName(createStatementDto.getSignatoryName());
        reconciliationStatementDto.setDeptId(createStatementDto.getSaleManagerDeptId());
        reconciliationStatementDto.setSaleManagerId(createStatementDto.getSaleManagerId());
        reconciliationStatementDto.setSaleManagerName(createStatementDto.getSaleManagerName());
        log.info("createStatement reconciliationStatementDto:{}", JSON.toJSONString(reconciliationStatementDto));

        if (StatementActionTypeEnum.PRE.getValue().equals(createStatementDto.getActionType())) {
            /*只生产Excel，不保存，只返回前台*/
            /*封装excel数据*/
            RentalStatementExportDto rentalStatementExportDto = defaultExcelExportPackager(reconciliationStatementDto);
            rentalStatementExportDto.setStatementType(createStatementRequest.getStatementType());//设置账单类型
            log.info("createStatement rentalStatementExportDto:{}", JSON.toJSONString(rentalStatementExportDto));
            String ossUrl = createContractStatementExcel(rentalStatementExportDto);
            createStatementResponse.setOssUrl(ossUrl);
        }

        if (StatementActionTypeEnum.SAVE.getValue().equals(createStatementDto.getActionType())) {
            /*生生成Excel，并保存数据*/
            RentalStatementExportDto rentalStatementExportDto = defaultExcelExportPackager(reconciliationStatementDto);
            reconciliationStatementDto.setStatementStatus(StatementStatusEnum.UN_NEED_SIGN);//对账单状态进行设置
            rentalStatementExportDto.setStatementType(createStatementRequest.getStatementType());//设置账单类型
            log.info("createStatement rentalStatementExportDto:{}", JSON.toJSONString(rentalStatementExportDto));
            String ossUrl = createContractStatementExcel(rentalStatementExportDto);
            Long statementId = identifierGenerator.nextId(rentalStatementExportDto).longValue();
            /*保存账单信息到数据库*/
            insertStatement(reconciliationStatementDto, createStatementDto, statementId, ossUrl);
            createStatementResponse.setOssUrl(ossUrl);
            createStatementResponse.setStatementId(statementId);
        }

        if (StatementActionTypeEnum.SIGN.getValue().equals(createStatementDto.getActionType())) {
            /*生生成Excel，保存账单数据并发起合同签署*/
            RentalStatementExportDto rentalStatementExportDto = defaultExcelExportPackager(reconciliationStatementDto);
            log.info("createStatement rentalStatementExportDto:{}", JSON.toJSONString(rentalStatementExportDto));
            rentalStatementExportDto.setStatementType(createStatementRequest.getStatementType());//设置账单类型
            String ossUrl = createContractStatementExcel(rentalStatementExportDto);
            Long statementId = identifierGenerator.nextId(rentalStatementExportDto).longValue();

            /*发起合同签署动作*/
            CreateContractRequest createContractRequest = new CreateContractRequest();
            createContractRequest.setRelationId(statementId);//账单id
            createContractRequest.setRelationNo(statementId.toString());//账单编号
            List<ContractActorRequest> contractActors = new ArrayList<>();
            ContractActorRequest contractActorRequest = new ContractActorRequest();
            contractActorRequest.setActorType(ContractActorTypeEnum.PERSON.getType());
            contractActorRequest.setActorId("参与方1");
            contractActorRequest.setActorName(createStatementDto.getSignatoryName());
            contractActorRequest.setActorPhone(createStatementDto.getSignatoryPhone());
            contractActorRequest.setActorIdCardNo(createStatementDto.getSignatoryIdCardNo());
            contractActors.add(contractActorRequest);
            createContractRequest.setContractActorList(contractActors);
            createContractRequest.setContractFileUrl(ossUrl);
            createContractRequest.setContractFileModel(ContractFileModelEnum.File.getType());
            createContractRequest.setType(ContractTypeEnum.ELECTRONIC_CONTRACT.getType());
            createContractRequest.setBusinessType(ContractBusinessTypeEnum.STATEMENT_OF_ACCOUNT_ELECTRONIC_CONTRACT.getType());
            ContractBusinessInfoRequest contractBusinessInfoRequest = new ContractBusinessInfoRequest();
            contractBusinessInfoRequest.setTaskSubject("账单签署");
            createContractRequest.setContractBusinessInfo(contractBusinessInfoRequest);
            CreateContractResponse contract = contractFeignClientRpc.createContract(createContractRequest);

            reconciliationStatementDto.setStatementStatus(StatementStatusEnum.NOT_SIGNED);
            reconciliationStatementDto.setContractId(contract.getContractId());
            reconciliationStatementDto.setContractNo(contract.getContractNo());
            /*保存账单信息到数据库*/
            insertStatement(reconciliationStatementDto, createStatementDto, statementId, ossUrl);
            createStatementResponse.setOssUrl(ossUrl);
            createStatementResponse.setStatementId(statementId);
        }
        log.info("createStatement response:{}", JSON.toJSONString(createStatementResponse));
        return createStatementResponse;
    }


    @Transactional
    public Boolean insertStatement(ReconciliationStatementDto reconciliationStatementDto, CreateStatementDto createStatementDto, Long statementId, String ossUrl) {
        List<ContractProFitLossDto> contractProFitLosses = reconciliationStatementDto.getContractProFitLosses();
        List<Long> lossIds = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(contractProFitLosses)) {
            lossIds = contractProFitLosses.stream().map(ContractProFitLossDto::getProfitLossId).distinct().collect(Collectors.toList());
        }
        ReconciliationStatementPo reconciliationStatementPo = BeanUtil.copyProperties(reconciliationStatementDto, ReconciliationStatementPo.class);
        reconciliationStatementPo.setStatementNo(statementNoGenerator());//账单编号
        reconciliationStatementPo.setOrderContractId(createStatementDto.getOrderContractId());
        reconciliationStatementPo.setOrderContractNo(createStatementDto.getOrderContractNo());
        reconciliationStatementPo.setProfitLossIds(JSON.toJSONString(lossIds));
        reconciliationStatementPo.setCustomerExtension(reconciliationStatementDto.getCustomerExtensionInfo());
        reconciliationStatementPo.setStatementId(statementId);
        reconciliationStatementPo.setStatementOssUrl(ossUrl);
        reconciliationStatementPo.setUpdaterId(createStatementDto.getUpdaterId());
        reconciliationStatementPo.setCreatorId(createStatementDto.getCreatorId());
        reconciliationStatementPo.setCreatorName(createStatementDto.getCreatorName());
        reconciliationStatementPo.setUpdaterName(createStatementDto.getUpdaterName());
        reconciliationStatementMapper.insert(reconciliationStatementPo);
        List<ReconciliationDeviceStatementPo> deviceRentalStatementDetails = BeanUtil.copyToList(reconciliationStatementDto.getDeviceRentalStatementDetails(), ReconciliationDeviceStatementPo.class);
        deviceRentalStatementDetails.forEach(p -> {
            Long id = identifierGenerator.nextId(p).longValue();
            p.setId(id);
            p.setCustomerId(reconciliationStatementDto.getCustomerId());
            p.setReconciliationContractStatementId(reconciliationStatementDto.getContractId());
            p.setReconciliationStatementId(statementId);
            p.setReconciliationDate(reconciliationStatementDto.getStatementDate());
            p.setUpdaterId(createStatementDto.getUpdaterId());
            p.setCreatorId(createStatementDto.getCreatorId());
            p.setCreatorName(createStatementDto.getCreatorName());
            p.setUpdaterName(createStatementDto.getUpdaterName());
        });
        reconciliationDeviceStatementMapper.batchInsertDevices(deviceRentalStatementDetails);
        return true;
    }

    @Override
    public CalculateStatementResponse calculateStatementDeviceStartTime(CalculateStatementRequest calculateStatementRequest) {
        //获取设备账单开始时间
        log.info("calculateStatementDeviceStartTime request:{}", JSON.toJSONString(calculateStatementRequest));
        CalculateStatementDto createStatementDto = BeanUtil.copyProperties(calculateStatementRequest, CalculateStatementDto.class);

        List<CalculateStatementDeviceDto> deviceDetails = createStatementDto.getDeviceDetails();
        List<DeviceRentalRecordPo> deviceRentalRecordParams = new ArrayList<>();
        deviceDetails.forEach(d -> {
            DeviceRentalRecordPo deviceRentalRecordPo = new DeviceRentalRecordPo();
            deviceRentalRecordPo.setId(d.getDeviceRentalRecordId());
            deviceRentalRecordPo.setDeviceNo(d.getDeviceNo());
            deviceRentalRecordPo.setContractNo(d.getContractNo());
            deviceRentalRecordParams.add(deviceRentalRecordPo);
        });
        List<DeviceRentalRecordPo> deviceRentalRecordPos = deviceRentalRecordMapper.selectDeviceByDeviceNoAndContractNo(deviceRentalRecordParams);
        log.info("selectDeviceByDeviceNoAndContractNo response:{}", JSON.toJSONString(deviceRentalRecordPos));

        /*月结设备开始时间list*/
        List<LocalDateTime> monthStartTimes = new ArrayList<>();
        /*月结设备结束时间list*/
        List<LocalDateTime> monthEndTimes = new ArrayList<>();

        /*期结设备开始时间list*/
        List<LocalDateTime> periodStartTimes = new ArrayList<>();
        /*期结设备结束时间list*/
        List<LocalDateTime> periodEndTimes = new ArrayList<>();

        /*总账单设备开始时间list*/
        List<LocalDateTime> totalStartTimes = new ArrayList<>();
        /*总账单设备结束时间list*/
        List<LocalDateTime> totalEndTimes = new ArrayList<>();

        deviceRentalRecordPos.forEach(deviceRentalRecordPo -> {
            deviceDetails.stream().filter(d -> d.getDeviceRentalRecordId().equals(deviceRentalRecordPo.getId())).findFirst().ifPresent(d -> {
                /*月结*/
                if (deviceRentalRecordPo.getLastReconciliationTime().isEqual(deviceRentalRecordPo.getRentalStartTime())) {
                    //如果是首次，则去上次截止时间-即起租时间
                    d.setCurrentRentalStartTime(deviceRentalRecordPo.getLastReconciliationTime());
                    d.setLastReconciliationTime(deviceRentalRecordPo.getLastReconciliationTime());
                } else {
                    //如果不是首次，则去上次截止时间+1秒
                    d.setCurrentRentalStartTime(deviceRentalRecordPo.getLastReconciliationTime().plusSeconds(1));
                    d.setLastReconciliationTime(deviceRentalRecordPo.getLastReconciliationTime().plusSeconds(1));
                }
                d.setDeviceBeginRentTime(deviceRentalRecordPo.getRentalStartTime());

                //step1.月结:结束时间设置为当月月底。开始时间按设备的上次截止时间最早的取值。需要对上次截止时间晚于当前月底的设备进行告警处理。
                monthStatementHandler.calculateStatementCycle(d);
                monthStartTimes.add(d.getCurrentRentalStartTime());
                monthEndTimes.add(d.getCurrentRentalEndTime());

                /*期结*/
                //step2.期结:开始时间按设备的上次截止时间最早的取值，结束时间为：待定。
                periodStatementHandler.calculateStatementCycle(d);
                periodStartTimes.add(d.getCurrentRentalStartTime());
                periodEndTimes.add(d.getCurrentRentalEndTime());

                /*总账单*/
                //step3.总账单时间：开始时间是设备最早的起租时间，结束时间随意。
                historyStatementHandler.calculateStatementCycle(d);
                totalStartTimes.add(d.getCurrentRentalStartTime());
                totalEndTimes.add(d.getCurrentRentalEndTime());
            });
        });

        CalculateStatementResponse calculateStatementResponse = new CalculateStatementResponse();

        List<StatementTypeTimeCalculate> statementTypeTimeCalculates = new ArrayList<>();
        StatementTypeTimeCalculate monthlyTime = new StatementTypeTimeCalculate();
        monthlyTime.setStatementType(StatementTypeEnum.MONTHLY.getValue());
        monthlyTime.setStatementTypeDesc(StatementTypeEnum.MONTHLY.getDesc());
        monthlyTime.setStatementStartTime(monthStartTimes.stream().min(LocalDateTime::compareTo).get());
        monthlyTime.setStatementEndTime(monthEndTimes.stream().max(LocalDateTime::compareTo).get());
        statementTypeTimeCalculates.add(monthlyTime);

        StatementTypeTimeCalculate periodicTime = new StatementTypeTimeCalculate();
        periodicTime.setStatementType(StatementTypeEnum.PERIODIC.getValue());
        periodicTime.setStatementTypeDesc(StatementTypeEnum.PERIODIC.getDesc());
        periodicTime.setStatementStartTime(periodStartTimes.stream().min(LocalDateTime::compareTo).get());
        periodicTime.setStatementEndTime(periodEndTimes.stream().max(LocalDateTime::compareTo).get());
        statementTypeTimeCalculates.add(periodicTime);

        StatementTypeTimeCalculate totalTime = new StatementTypeTimeCalculate();
        totalTime.setStatementType(StatementTypeEnum.TOTAL.getValue());
        totalTime.setStatementTypeDesc(StatementTypeEnum.TOTAL.getDesc());
        totalTime.setStatementStartTime(totalStartTimes.stream().min(LocalDateTime::compareTo).get());
        totalTime.setStatementEndTime(null);//目前不传，前端自由选取
        statementTypeTimeCalculates.add(totalTime);
        calculateStatementResponse.setStatementTypeTimeCalculates(statementTypeTimeCalculates);

        return calculateStatementResponse;
    }

    @Override
    public InTransitDocumentInfoResponse getInTransitDocumentInfos(InTransitDocumentInfoRequest inTransitDocumentInfoRequest) {
        InTransitDocumentInfoDto inTransitDocumentInfoDto = BeanUtil.copyProperties(inTransitDocumentInfoRequest, InTransitDocumentInfoDto.class);

        /*根据联系人和客户id查询订单数据*/
        List<Long> contractIds;
        //如果前端有指定合同id，则使用前端数据
        if (Objects.nonNull(inTransitDocumentInfoDto.getContractId())) {
            contractIds = Arrays.asList(inTransitDocumentInfoDto.getContractId());
        } else {
            //如果前端没有指定合同id，则按联系人和签约人查询名下所有合适的contractIds
            List<OrderPrimaryApiResponse> orderPrimaryApiResponses = orderFeignClientRpc.getOrderInforsByCustomerInfo(inTransitDocumentInfoDto.getCustomerId(), inTransitDocumentInfoDto.getSignatoryId());
            contractIds = orderPrimaryApiResponses.stream().map(OrderPrimaryApiResponse::getContractId).collect(Collectors.toList());
        }

        /*step1.索赔单数量查询*/
        CountUnFinishClaimRequest countUnFinishClaimRequest = new CountUnFinishClaimRequest();
        countUnFinishClaimRequest.setCustomerId(inTransitDocumentInfoDto.getCustomerId());
        countUnFinishClaimRequest.setSignatoryId(inTransitDocumentInfoDto.getSignatoryId());
        if (Objects.nonNull(inTransitDocumentInfoDto.getContractId())) {
            countUnFinishClaimRequest.setContractId(inTransitDocumentInfoDto.getContractId());
        }
        Integer claimCount = fulfillmentFeignClientRpc.getClaimCountByCustomerInfo(countUnFinishClaimRequest);

        /*step2.报停单数据查询*/
        CountUnFinishPauseRequest countUnFinishPauseRequest = new CountUnFinishPauseRequest();
        countUnFinishPauseRequest.setCustomerId(inTransitDocumentInfoDto.getCustomerId());
        countUnFinishPauseRequest.setSignatoryId(inTransitDocumentInfoDto.getSignatoryId());
        if (Objects.nonNull(inTransitDocumentInfoDto.getContractId())) {
            countUnFinishPauseRequest.setContractId(inTransitDocumentInfoDto.getContractId());
        }
        Integer pauseCount = fulfillmentFeignClientRpc.getPauseCountByCustomerInfo(countUnFinishPauseRequest);

        /*step3.设备在租状态数据查询*/
        Integer deviceInRentalCount = 0;
        if (CollectionUtil.isNotEmpty(contractIds)) {
            LambdaQueryWrapper<DeviceRentalRecordPo> deviceRentalRecordPoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            deviceRentalRecordPoLambdaQueryWrapper.in(DeviceRentalRecordPo::getContractId, contractIds);
            deviceRentalRecordPoLambdaQueryWrapper.isNull(DeviceRentalRecordPo::getRentalEndTime);
            deviceInRentalCount = deviceRentalRecordMapper.selectCount(deviceRentalRecordPoLambdaQueryWrapper);
        }

        /*step4.应收调整单数量查询*/
        Integer receiveAdjustCount;
        LambdaQueryWrapper<ReceiveAdjustPo> receiveAdjustPoLambdaQueryWrapper = new LambdaQueryWrapper<>();
//        receiveAdjustPoLambdaQueryWrapper.eq(ReceiveAdjustPo::getCustomerId, inTransitDocumentInfoDto.getCustomerId());
//        receiveAdjustPoLambdaQueryWrapper.eq(ReceiveAdjustPo::getSignatoryId, inTransitDocumentInfoDto.getSignatoryId());
        if (CollectionUtil.isNotEmpty(contractIds)) {
            receiveAdjustPoLambdaQueryWrapper.in(ReceiveAdjustPo::getContractId, contractIds);
        }
        receiveAdjustPoLambdaQueryWrapper.eq(ReceiveAdjustPo::getStatus, 10);
        receiveAdjustPoLambdaQueryWrapper.eq(ReceiveAdjustPo::getIsDeleted, 0);
        receiveAdjustCount = receiveAdjustMapper.selectCount(receiveAdjustPoLambdaQueryWrapper);

        /*step5.退场单数量查询*/
        Integer exitFormCount = 0;
        if (CollectionUtil.isNotEmpty(contractIds)) {
            ExitingFormCountRequest exitingFormCountRequest = new ExitingFormCountRequest();
            exitingFormCountRequest.setContractIdList(contractIds);
            ExitingFormCountResponse response = fulfillmentFeignClientRpc.queryExitingFormCount(exitingFormCountRequest);
            exitFormCount = response.getCount().intValue();
        }

        InTransitDocumentInfoResponse response = new InTransitDocumentInfoResponse();
        response.setReceiveAdjustCount(receiveAdjustCount);
        response.setDeviceInRentalCount(deviceInRentalCount);
        response.setPauseCount(pauseCount);
        response.setClaimCount(claimCount);
        response.setExitFormCount(exitFormCount);
        return response;
    }


    @Override
    @Transactional
    public Boolean updateContractSignedInfos(FinancialContractUpdateApiRequest financialContractUpdateApiRequest) {
        log.info("IReconciliationStatementService#updateContractSignedInfos request:{}", JSON.toJSONString(financialContractUpdateApiRequest));
        Long relationId = financialContractUpdateApiRequest.getRelationId();
        String contractSignedUrl = financialContractUpdateApiRequest.getContractSignedUrl();

        /*根据对账单id查询对账单详情*/
        /*step.1 查询出账单信息(设备对账金额及总对账金额&调整损益单list)*/
        LambdaQueryWrapper<ReconciliationStatementPo> statementQueryWrapper = new LambdaQueryWrapper<>();
        statementQueryWrapper.eq(ReconciliationStatementPo::getStatementId, relationId);
        statementQueryWrapper.eq(ReconciliationStatementPo::getStatementStatus, StatementStatusEnum.NOT_SIGNED);
        ReconciliationStatementPo reconciliationStatementRo = reconciliationStatementMapper.selectOne(statementQueryWrapper);
        if (Objects.isNull(reconciliationStatementRo)) {
            log.info("IReconciliationStatementService#updateContractSignedInfos reconciliationStatementPo is null,relationId:{}", relationId);
            return Boolean.FALSE;
        }
        log.info("IReconciliationStatementService#updateContractSignedInfos reconciliationStatementPo is null,relationId:{}", JSON.toJSONString(reconciliationStatementRo));

        List<Long> deviceLossIds = new ArrayList<>();
        LambdaQueryWrapper<ReconciliationDeviceStatementPo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(ReconciliationDeviceStatementPo::getReconciliationStatementId, relationId);
        queryWrapper.eq(ReconciliationDeviceStatementPo::getIsDeleted, 0);
        List<ReconciliationDeviceStatementPo> reconciliationDeviceStatementRos = reconciliationDeviceStatementMapper.selectList(queryWrapper);
        reconciliationDeviceStatementRos.forEach(r -> {
            if (CollectionUtil.isNotEmpty(r.getDeviceLossInfos())) {
                deviceLossIds.addAll(r.getDeviceLossInfos().stream().map(l -> l.getDeviceLossId()).collect(Collectors.toList()));
            }
        });

        /*根据id更新合同签约地址*/
        LambdaQueryWrapper<ReconciliationStatementPo> updateWrapper = new LambdaQueryWrapper<>();
        updateWrapper.eq(ReconciliationStatementPo::getStatementId, relationId);
        ReconciliationStatementPo reconciliationStatement = new ReconciliationStatementPo();
        reconciliationStatement.setContractSignedUrl(contractSignedUrl);
        reconciliationStatement.setStatementStatus(StatementStatusEnum.SIGNED);
        reconciliationStatement.setContractSignTime(LocalDateTime.now());
        reconciliationStatementMapper.update(reconciliationStatement, updateWrapper);

        /*设备上次对账时间更新*/
        /*设备租赁已对账金额更新:总账单就比大小更新总金额，月结和期结就累加*/
        List<DeviceRentalRecordUpdatePo> deviceRentalRecordPos = new ArrayList<>();
        reconciliationDeviceStatementRos.forEach(reconciliationDeviceStatementPo -> {
            DeviceRentalRecordUpdatePo deviceRentalRecordPo = new DeviceRentalRecordUpdatePo();
            deviceRentalRecordPo.setDeviceId(reconciliationDeviceStatementPo.getDeviceId());
            deviceRentalRecordPo.setContractId(reconciliationDeviceStatementPo.getContractId());
            deviceRentalRecordPo.setDeviceRentalRecordId(reconciliationDeviceStatementPo.getDeviceRentalRecordId());
            deviceRentalRecordPo.setLastReconciliationTime(reconciliationStatementRo.getReconciliationEndDate());
            deviceRentalRecordPo.setReconciledAmount(reconciliationDeviceStatementPo.getTotalAmount());
            deviceRentalRecordPos.add(deviceRentalRecordPo);
        });
        log.info("IReconciliationStatementService#updateContractSignedInfos deviceRentalRecordPos :{}", JSON.toJSON(deviceRentalRecordPos));

        List<Long> lossIds = null;
        if (StringUtils.isNotEmpty(reconciliationStatementRo.getProfitLossIds())) {
            log.info("ProfitLossIds:{}", JSON.toJSONString(reconciliationStatementRo.getProfitLossIds()));
            lossIds = JSON.parseArray(reconciliationStatementRo.getProfitLossIds(), Long.class);
            log.info("lossIds:{}", JSON.toJSONString(lossIds));
        }

        if (StatementTypeEnum.TOTAL.equals(reconciliationStatementRo.getStatementType())) {
            /*step1.计算当然账单费用的总和*/
            BigDecimal currentReconciledAmount = deviceRentalRecordPos.stream().map(DeviceRentalRecordUpdatePo::getReconciledAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            //取得调整损益的总额
            BigDecimal adjustTotalAmount = BigDecimal.ZERO;
            if (CollectionUtil.isNotEmpty(lossIds)) {
                LambdaQueryWrapper<ProfitLossRecordPo> profitLossQueryWrapper = new LambdaQueryWrapper<>();
                profitLossQueryWrapper.in(ProfitLossRecordPo::getId, lossIds);
                List<ProfitLossRecordPo> recordRos = profitLossRecordMapper.selectList(profitLossQueryWrapper);
                List<ContractProFitLossDto> contractProFitLossList = new ArrayList<>();
                ReconciliationAdjustJson reconciliationAdjustJson = historyStatementHandler.getReconciliationAdjustJson(recordRos, contractProFitLossList);
                adjustTotalAmount = reconciliationAdjustJson.getAdjustTotal();
            }

            BigDecimal currentTotalAmount = currentReconciledAmount.add(adjustTotalAmount);

            /* step2.获取历史账单总金额*/
            BigDecimal historyReconciledAmount = receivableInventoryMapper.queryHistoryReconciledAmountByContractIdId(reconciliationStatementRo.getOrderContractId());

            /*step3.如果这次的比之前的金额大，则进行更新。*/
            if (currentReconciledAmount.compareTo(historyReconciledAmount) > 0) {
                /*如果这次的比之前的金额大，则进行更新。*/
                //更新租赁记录表(没有调整数据)
                deviceRentalRecordMapper.updateReconciledAmountAndLastReconciliationTimeByHistory(deviceRentalRecordPos);
                //更新盘点记录表(有调整数据)
                LambdaQueryWrapper<ReceivableInventoryPo> receivableInventoryUpdateWrapper = new LambdaQueryWrapper<>();
                receivableInventoryUpdateWrapper.eq(ReceivableInventoryPo::getContractId, reconciliationStatementRo.getOrderContractId());
                ReceivableInventoryPo receivableInventoryPo = new ReceivableInventoryPo();
                receivableInventoryPo.setHistoryReconciliationedAmount(currentTotalAmount);
                receivableInventoryMapper.update(receivableInventoryPo, receivableInventoryUpdateWrapper);
            }
        } else {
            /*月结&期结账单*/
            //如果是月账单&期结(月结)账单，则更新累加总金额
            deviceRentalRecordMapper.updateReconciledAmountAndLastReconciliationTimeByPeriod(deviceRentalRecordPos);
            //如果是期结(月结)，对损益单据进行更新
            if (CollectionUtil.isNotEmpty(lossIds)) {
                LambdaQueryWrapper<ProfitLossRecordPo> updateLossWrapper = new LambdaQueryWrapper<>();
                updateLossWrapper.in(ProfitLossRecordPo::getId, lossIds);
                ProfitLossRecordPo profitLossRecordPo = new ProfitLossRecordPo();
                profitLossRecordPo.setStatus(ProfitLossStatusEnum.RECONCILED);
                profitLossRecordMapper.update(profitLossRecordPo, updateLossWrapper);
            }
            /*对设备纬度的损益单记录进行更新*/
            if (CollectionUtil.isNotEmpty(deviceLossIds)) {
                LambdaQueryWrapper<DeviceProfitLossDetailPo> updateDeviceLossWrapper = new LambdaQueryWrapper<>();
                updateDeviceLossWrapper.in(DeviceProfitLossDetailPo::getId, deviceLossIds);
                DeviceProfitLossDetailPo deviceProfitLossDetailPo = new DeviceProfitLossDetailPo();
                deviceProfitLossDetailPo.setStatus(ProfitLossStatusEnum.RECONCILED);
                deviceProfitLossDetailMapper.update(deviceProfitLossDetailPo, updateDeviceLossWrapper);
            }

            //对整个合同的已对账金额进行求和计算
            //对deviceRentalRecordPos中的reconciledAmount进行求和
            BigDecimal currentReconciledAmount = deviceRentalRecordPos.stream().map(DeviceRentalRecordUpdatePo::getReconciledAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal historyTotalReconciledAmount = deviceRentalRecordMapper.selectTotalReconciledAmountByContractId(reconciliationStatementRo.getOrderContractId());
            historyTotalReconciledAmount = Objects.isNull(historyTotalReconciledAmount) ? BigDecimal.ZERO : historyTotalReconciledAmount;
            //取得调整损益的总额:之前账单已经对过的+本群的调整单
            LambdaQueryWrapper<ProfitLossRecordPo> profitLossQueryWrapper = new LambdaQueryWrapper<>();
            profitLossQueryWrapper.and(wra -> {
                wra.eq(ProfitLossRecordPo::getBusinessBillType, BusinessBillTypeEnum.RECEIVABLE_ADJUSTMENT);
                wra.eq(ProfitLossRecordPo::getStatus, ProfitLossStatusEnum.RECONCILED);
                wra.eq(ProfitLossRecordPo::getContractId, reconciliationStatementRo.getOrderContractId());
            });
            if (CollectionUtil.isNotEmpty(lossIds)) {
                profitLossQueryWrapper.or();
                profitLossQueryWrapper.in(ProfitLossRecordPo::getId, lossIds);
            }
            List<ProfitLossRecordPo> recordRos = profitLossRecordMapper.selectList(profitLossQueryWrapper);
            List<ContractProFitLossDto> contractProFitLossList = new ArrayList<>();
            ReconciliationAdjustJson reconciliationAdjustJson = historyStatementHandler.getReconciliationAdjustJson(recordRos, contractProFitLossList);
            BigDecimal adjustTotalAmount = reconciliationAdjustJson.getAdjustTotal();
            //对应收盘点单据进行更新已对账金额,记得加调整金额
            BigDecimal totalAmount = historyTotalReconciledAmount.add(adjustTotalAmount);
            //对应收盘点表进行数据更新
            LambdaQueryWrapper<ReceivableInventoryPo> receivableInventoryUpdateWrapper = new LambdaQueryWrapper<>();
            receivableInventoryUpdateWrapper.eq(ReceivableInventoryPo::getContractId, reconciliationStatementRo.getOrderContractId());
            ReceivableInventoryPo receivableInventoryPo = new ReceivableInventoryPo();
            receivableInventoryPo.setTotalReconciliationedAmount(totalAmount);
            receivableInventoryMapper.update(receivableInventoryPo, receivableInventoryUpdateWrapper);
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional
    public Boolean updateContractCreateInfos(FinancialContractUpdateApiRequest financialContractUpdateApiRequest) {
        log.info("IReconciliationStatementService#updateContractCreateInfos request:{}", JSON.toJSONString(financialContractUpdateApiRequest));
        Long relationId = financialContractUpdateApiRequest.getRelationId();
        String contractCreatedUrl = financialContractUpdateApiRequest.getContractCreatedUrl();
        Long contractId = financialContractUpdateApiRequest.getContractId();
        LambdaQueryWrapper<ReconciliationStatementPo> updateWrapper = new LambdaQueryWrapper<>();
        updateWrapper.eq(ReconciliationStatementPo::getStatementId, relationId);
        ReconciliationStatementPo reconciliationStatement = new ReconciliationStatementPo();
        reconciliationStatement.setContractCreatedUrl(contractCreatedUrl);
        reconciliationStatement.setContractId(contractId);
        reconciliationStatementMapper.update(reconciliationStatement, updateWrapper);
        return Boolean.TRUE;
    }

    /**
     * 根据模板创建Excel并上传到oss
     */
    private String createContractStatementExcel(RentalStatementExportDto rentalStatementExportDto) {
        /*封装数据*/
        List<DeviceRentalStatementExportDto> deviceRentalExportData = rentalStatementExportDto.getDeviceRentalDetails();
        HashMap<String, Object> map = rentalStatementExportDto.getStatementInfoMap();

        String excelTemplatePath;
        if (StatementTypeEnum.TOTAL.getValue().equals(rentalStatementExportDto.getStatementType())) {
            excelTemplatePath = "finance/exceltemplate/totalCustomerStatementTemplate.xlsx";//总账单模板
        } else {
            excelTemplatePath = "finance/exceltemplate/customerStatementTemplate.xlsx";//非总账单模板
        }

        InputStream exceltemplateInputStream;
        exceltemplateInputStream = ossStsUtil.downloadFile(excelTemplatePath);

        /*进行Excel写入*/
        String fileName = "账单导出" + LocalDateTime.now() + ".xlsx";
        File file = new File(fileName);
        log.info("templateFileName:{}", excelTemplatePath);
        try (ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(exceltemplateInputStream).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            excelWriter.fill(deviceRentalExportData, fillConfig, writeSheet);
            excelWriter.fill(map, writeSheet);
        }

        /*生成Excel文件流*/
        FileInputStream fileInputStream;
        try {
            fileInputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }

        XSSFWorkbook workbook;
        try {
            workbook = new XSSFWorkbook(fileInputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //插入图片
        String imgPath = "finance/exceltemplate/QR_Code.png";
        InputStream imgStream;
        imgStream = ossStsUtil.downloadFile(imgPath);
        insertImage(workbook, imgStream);

        /*上传oss,完成后删除本地文件*/
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            workbook.write(bos);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        try {
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String ossUrl = ossStsUtil.uploadByStream(bis, fileName);
        file.delete();
        return ossUrl;
    }

    private static void insertImage(XSSFWorkbook workbook, InputStream imgStream) {
        XSSFSheet sheet = workbook.getSheetAt(0);

        // 标记字符串
        String mark = "mark";
        int rowIndex = -1; // 初始化为-1表示未找到标记
        int colIndex = -1; // 初始化为-1表示未找到标记

        // 遍历第一个sheet的所有行和列，查找标记位置
        for (Row row : sheet) {
            for (Cell cell : row) {
                if (cell.getCellType() == CellType.STRING && mark.equals(cell.getStringCellValue())) {
                    rowIndex = cell.getRowIndex();
                    colIndex = cell.getColumnIndex();
                    break;
                }
            }
            if (rowIndex != -1 && colIndex != -1) {
                break; // 如果找到标记位置，跳出循环
            }
        }

        if (rowIndex == -1 || colIndex == -1) {
            return;
        }

        try {
            // 读取图片文件
//            File imageFile = new File(imgUrl);
            BufferedImage bufferedImage = ImageIO.read(imgStream);

            // 调整图片大小
            int newWidth = 220; // 设置新的宽度
            int newHeight = 220; // 设置新的高度
            BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = resizedImage.createGraphics();
            g.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null);
            g.dispose();

            // 将调整大小后的图片插入Excel
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(resizedImage, "png", byteArrayOutputStream);
            int pictureIdx = workbook.addPicture(byteArrayOutputStream.toByteArray(), Workbook.PICTURE_TYPE_JPEG);

            // 创建一个绘图对象
            XSSFDrawing drawing = sheet.createDrawingPatriarch();
            CreationHelper helper = workbook.getCreationHelper();

            // 创建一个锚点，并指定位置和大小
            ClientAnchor anchor = helper.createClientAnchor();
            anchor.setCol1(colIndex); // 图片在Excel中的列索引
            anchor.setRow1(rowIndex); // 图片在Excel中的行索引
            anchor.setDx1(120); // 向右偏移100个像素
            anchor.setDy1(120); // 向下偏移100个像素
//            anchor.setCol2(colIndex + 1); // 图片在Excel中的结束列索引
//            anchor.setRow2(rowIndex + 1); // 图片在Excel中的结束行索引

            // 在锚点位置插入图片
            XSSFPicture picture = drawing.createPicture(anchor, pictureIdx);
            picture.resize(); // 调整图片大小以适应单元格

            byteArrayOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 默认的Excel导出包装器
     */
    public RentalStatementExportDto defaultExcelExportPackager(ReconciliationStatementDto reconciliationStatementDto) {
        RentalStatementExportDto rentalStatementExportDto = new RentalStatementExportDto();
        /*设备信息封装*/
        //reconciliationStatementDto.getDeviceRentalStatementDetails()按rentalStartTime正序排序
        reconciliationStatementDto.getDeviceRentalStatementDetails().sort(Comparator.comparing(ReconciliationDeviceStatementDto::getRentalStartTime));

        List<DeviceRentalStatementExportDto> deviceRentalStatementExportDtos = new ArrayList<>();
        Integer serialNumber = 1;
        for (ReconciliationDeviceStatementDto dd : reconciliationStatementDto.getDeviceRentalStatementDetails()) {
            DeviceRentalStatementExportDto deviceRentalStatementExportDto = new DeviceRentalStatementExportDto();
            deviceRentalStatementExportDto.setSerialNumber(serialNumber);
            serialNumber++;//序号自增
            deviceRentalStatementExportDto.setContractNo(dd.getContractNo());
            deviceRentalStatementExportDto.setDeviceModel(dd.getDeviceModel());
            deviceRentalStatementExportDto.setDeviceStatusDesc(dd.getDeviceStatementStatusDesc());
            deviceRentalStatementExportDto.setDeviceNo(dd.getDeviceNo());
            if (dd.getUseMonthlyRentalPrice()) {
                deviceRentalStatementExportDto.setRentalUnitPrice(dd.getRentalPrice().setScale(0, BigDecimal.ROUND_HALF_UP).toString() + "元/月/台");
            } else {
                deviceRentalStatementExportDto.setRentalUnitPrice(dd.getRentalPrice().setScale(0, BigDecimal.ROUND_HALF_UP).toString() + "元/天/台");
            }
            deviceRentalStatementExportDto.setRentalStartTime(dd.getRentalStartTime());
            deviceRentalStatementExportDto.setRentalEndTime(dd.getRentalEndTime());
            deviceRentalStatementExportDto.setTotalDays(dd.getTotalDays().toString());
            deviceRentalStatementExportDto.setRentalDays(dd.getRentalDays().toString());
            deviceRentalStatementExportDto.setRentPayable(dd.getRentPayable().setScale(2, BigDecimal.ROUND_UP));//显示取两位小数，向上取数，下同

            if (reconciliationStatementDto.getDisplayEntryShippingFee2Excel() || Objects.nonNull(dd.getDeviceEndRentTime())) {
                deviceRentalStatementExportDto.setEntryFee(dd.getEntryFee().setScale(2, BigDecimal.ROUND_UP).toString());
            } else {
                deviceRentalStatementExportDto.setEntryFee("退场后结算" + "(" + dd.getEntryFee().setScale(2, BigDecimal.ROUND_UP).toString() + ")");
            }
            if (reconciliationStatementDto.getDisplayModificationFee2Excel() || Objects.nonNull(dd.getDeviceEndRentTime())) {
                deviceRentalStatementExportDto.setModificationPayable(dd.getModificationPayable().setScale(2, BigDecimal.ROUND_UP).toString());
            } else {
                deviceRentalStatementExportDto.setModificationPayable("退场后结算" + "(" + dd.getModificationPayable().setScale(2, BigDecimal.ROUND_UP).toString() + ")");
            }

            deviceRentalStatementExportDto.setExitFee(dd.getExitFee().setScale(2, BigDecimal.ROUND_UP));
            deviceRentalStatementExportDto.setClaimPayable(dd.getClaimPayable().setScale(2, BigDecimal.ROUND_UP));

            deviceRentalStatementExportDto.setHistoryPausePayable(dd.getPausePayable().setScale(2, BigDecimal.ROUND_UP));
            deviceRentalStatementExportDtos.add(deviceRentalStatementExportDto);
        }
        rentalStatementExportDto.setDeviceRentalDetails(deviceRentalStatementExportDtos);

        /*账单主信息封装*/
        HashMap<String, Object> statementInfoMap = rentalStatementExportDto.getStatementInfoMap();
        statementInfoMap.put("lesseeFirst", reconciliationStatementDto.getSignatoryName());
//        statementInfoMap.put("lesseeSecond", reconciliationStatementDto.getCustomerName());//todo:暂时不显示，后续私转公之后需要处理
        statementInfoMap.put("projectName", reconciliationStatementDto.getProjectName());
        DateTimeFormatter formatter01 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        statementInfoMap.put("statementDate", LocalDateTime.now().format(formatter01));

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        Integer days = iReconciliationStatementProviderService.calculateDaysBetween(reconciliationStatementDto.getReconciliationStartDate(), reconciliationStatementDto.getReconciliationEndDate()) + 1;
        statementInfoMap.put("reconciliationPeriod", reconciliationStatementDto.getReconciliationStartDate().format(formatter) + "至" + reconciliationStatementDto.getReconciliationEndDate().format(formatter) + "(" + days + "天)");
        statementInfoMap.put("rentPayableSubtotal", reconciliationStatementDto.getReconciliationSubtotal().getRentalFee().setScale(2, BigDecimal.ROUND_UP));//租金小计

        /*进场运费小计*/
        statementInfoMap.put("entryFeeSubtotal", reconciliationStatementDto.getReconciliationSubtotal().getDisplayEntryShippingFee().setScale(2, BigDecimal.ROUND_UP));//进场运费小计
        statementInfoMap.put("exitFeeSubtotal", reconciliationStatementDto.getReconciliationSubtotal().getExitShippingFee().setScale(2, BigDecimal.ROUND_UP));//退场运费小计
        statementInfoMap.put("claimPayableSubtotal", reconciliationStatementDto.getReconciliationSubtotal().getClaimFee().setScale(2, BigDecimal.ROUND_UP));//索赔小计

        /*改装小计,展示显示数据，不是真实数据*/
        statementInfoMap.put("modificationSubtotal", reconciliationStatementDto.getReconciliationSubtotal().getDisplayModificationFee().setScale(2, BigDecimal.ROUND_UP));//改装小计
        statementInfoMap.put("historyPauseSubtotal", reconciliationStatementDto.getReconciliationSubtotal().getPauseFee().setScale(2, BigDecimal.ROUND_UP));//补录报停小计
        statementInfoMap.put("rentPayableAdjustCost", reconciliationStatementDto.getReconciliationAdjust().getRentPayableAdjustCost().setScale(2, BigDecimal.ROUND_UP));//租金调整
        statementInfoMap.put("entryFeeAdjustCost", reconciliationStatementDto.getReconciliationAdjust().getEntryFeeAdjustCost().setScale(2, BigDecimal.ROUND_UP));//进场运费调整
        statementInfoMap.put("exitFeeAdjustCost", reconciliationStatementDto.getReconciliationAdjust().getExitFeeAdjustCost().setScale(2, BigDecimal.ROUND_UP));//退场运费调整
        statementInfoMap.put("historyPauseAdjustCost", BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_UP));//报停调整
        statementInfoMap.put("claimPayableAdjustCost", reconciliationStatementDto.getReconciliationAdjust().getClaimPayableAdjustCost().setScale(2, BigDecimal.ROUND_UP));//索赔调整
        statementInfoMap.put("modificationAdjustCost", reconciliationStatementDto.getReconciliationAdjust().getModificationAdjustCost().setScale(2, BigDecimal.ROUND_UP));//改装调整
        statementInfoMap.put("rentPayableTotal", reconciliationStatementDto.getReconciliationAmount().getRentalFee().setScale(2, BigDecimal.ROUND_UP));//租金合计(租金运费小计+租金调整)
        statementInfoMap.put("entryFeeTotal", reconciliationStatementDto.getReconciliationAmount().getDisplayEntryShippingFee().setScale(2, BigDecimal.ROUND_UP));//进场运费合计(进场运费小计+进场运费调整)
        statementInfoMap.put("exitFeeTotal", reconciliationStatementDto.getReconciliationAmount().getExitShippingFee().setScale(2, BigDecimal.ROUND_UP));//退场运费合计(退场运费小计+退场运费调整)
        statementInfoMap.put("claimPayableTotal", reconciliationStatementDto.getReconciliationAmount().getClaimFee().setScale(2, BigDecimal.ROUND_UP));//索赔合计(索赔运费小计+索赔调整)
        statementInfoMap.put("modificationTotal", reconciliationStatementDto.getReconciliationAmount().getDisplayModificationFee().setScale(2, BigDecimal.ROUND_UP));//改装合计(改装小计+改装调整)
        statementInfoMap.put("historyPauseTotal", reconciliationStatementDto.getReconciliationAmount().getPauseFee().setScale(2, BigDecimal.ROUND_UP));//改装合计
        statementInfoMap.put("totalFee", reconciliationStatementDto.getDisplayReconciliationTotalAmount().setScale(2, BigDecimal.ROUND_UP));//总费用
        statementInfoMap.put("saleManager", reconciliationStatementDto.getSaleManagerName());//销售经理名称
        statementInfoMap.put("reconciliationedTotalAmount", reconciliationStatementDto.getReconciliationedTotalAmount().setScale(2, BigDecimal.ROUND_UP));//已对账金额
        statementInfoMap.put("contractTotalAmount", reconciliationStatementDto.getContractTotalAmount().setScale(2, BigDecimal.ROUND_UP));//合同总费用
        statementInfoMap.put("arrearsCost", "-");

        String mark = StringUtils.isNotEmpty(reconciliationStatementDto.getMark()) ? reconciliationStatementDto.getMark() : "";
        statementInfoMap.put("mark", "备注：" + mark);//备注
        return rentalStatementExportDto;
    }

    private void setUserContextInfo(BaseDto baseDto) {
        ContextUserInfo userInfo = UserContextUtil.getCurrentUser();
        if (Objects.isNull(userInfo) || Objects.isNull(userInfo.getId())) {
            throw new HMYRuntimeException(5010018);
        }
        baseDto.setDeptIds(new ArrayList<>(userInfo.getDeptIds()));
        baseDto.setRoles(userInfo.getRoles());
        baseDto.setUpdaterId(userInfo.getId());
        baseDto.setUpdaterName(userInfo.getUserName());
        baseDto.setCreatorId(userInfo.getId());
        baseDto.setCreatorName(userInfo.getUserName());

    }

    private void setUserInfoContextInfo(HistoryStatementQueryDto historyStatementQueryDto) {
        ContextUserInfo userInfo = UserContextUtil.getCurrentUser();
        if (Objects.isNull(userInfo) || Objects.isNull(userInfo.getId()) || Objects.isNull(userInfo.getRoles())) {
            throw new HMYRuntimeException(506000000);
        }
        historyStatementQueryDto.setRoles(userInfo.getRoles());
        //当前用户为销售经理，则设置销售经理id
        if (historyStatementQueryDto.getRoles().contains(RoleEnum.SALE_MANAGER.getRoleType())) {
            historyStatementQueryDto.setSaleManagerId(userInfo.getId());
        }
        //当前用户为门店行政或者区域经理，则设置部门list为这个人的部门list
        if (historyStatementQueryDto.getRoles().contains(RoleEnum.AREA_MANAGER.getRoleType()) || historyStatementQueryDto.getRoles().contains(RoleEnum.CLERK.getRoleType())) {
            historyStatementQueryDto.setDeptIds(new ArrayList<>(userInfo.getDeptIds()));
        }

    }

    private String statementNoGenerator() {
        String currentDate = DateUtil.format(new Date(), DatePattern.createFormatter("yyMMdd"));
        //生成一个6位随机数字
        String statementNo = "ZD" + currentDate + RandomUtil.randomNumbers(6);
        return statementNo;
    }

}




